﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace com.lover.common.hook
{
    public delegate void DoKeyHook();
    public delegate int HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam);
    public class HookUtils
    {
        public enum HookType
        {
            WH_KEYBOARD = 2,//私有钩子
            WH_KEYBOARD_LL = 13//全局钩子
        }
        public struct KeyBoardHookStruct
        {
            public int vkCode;
            public int ScanCode;
            public int Flags;
            public int Time;
            public int DwExtraInfo;
        }
        //设置钩子
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(HookType idHook, HOOKPROC lpfn, IntPtr hInstance, int threadId);
        //抽调钩子
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("kernel32")]
        private static extern int GetCurrentThreadId();

        private static bool _hookShift = true;
        private static bool _hookCtrl = true;
        private static bool _hookAlt = true;
        private static Keys _hookKey = 0;
        private static int _hookHandle = 0;

        private static HOOKPROC _keyProc = new HOOKPROC(HookKeyboardProc);
        private static DoKeyHook _hookTrueFunc;
        public static void removeKeyHook()
        {
            if (_hookHandle != 0) { UnhookWindowsHookEx(_hookHandle); _hookHandle = 0; }
        }
        public static void setKeyHook(bool doShift, bool doCtrl, bool doAlt, Keys keycode, DoKeyHook func)
        {
            _hookCtrl = doCtrl; _hookAlt = doAlt; _hookShift = doShift; _hookKey = keycode;
            _hookTrueFunc = func;
            if (_hookHandle != 0) { UnhookWindowsHookEx(_hookHandle); _hookHandle = 0; }
            _hookHandle = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, _keyProc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
        }
        public static int HookKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (_hookHandle == 0) return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
            bool isPressed = (lParam.ToInt32() & 0x80000000) == 0;
            if (nCode < 0 || !isPressed) return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
            KeyStateInfo ctrlKey = KeyboardInfo.GetKeyState(Keys.ControlKey);
            KeyStateInfo altKey = KeyboardInfo.GetKeyState(Keys.Alt);
            KeyStateInfo shiftKey = KeyboardInfo.GetKeyState(Keys.ShiftKey);
            KeyStateInfo hk = KeyboardInfo.GetKeyState(_hookKey);
            if (hk.IsPressed &&
                ((_hookCtrl && ctrlKey.IsPressed) || !_hookCtrl) &&
                ((_hookAlt && altKey.IsPressed) || !_hookAlt) &&
                ((_hookShift && shiftKey.IsPressed) || !_hookShift)
                )
            {
                if (_hookTrueFunc != null) _hookTrueFunc.Invoke();
            }
            return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
        }
        public struct KBDLLHOOKSTRUCT
        {
            public UInt32 vkCode;
            public UInt32 scanCode;
            public UInt32 flags;
            public UInt32 time;
            public IntPtr extraInfo;
        } 
    }
}
